package per.vic.attachment.service;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import per.vic.attachment.annotation.AttachmentFlag;
import per.vic.attachment.autoconfigure.AttachmentProperties;
import per.vic.attachment.dao.BaseAttachmentDao;
import per.vic.attachment.model.Attachment;
import pers.vic.boot.util.CnToSpellUtils;
import pers.vic.boot.util.CommonUtils;
import pers.vic.boot.util.RegexUtil;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @description: 附件处理service
 * @author: Vic.xu
 * @date: 2019年12月11日 下午1:37:30
 * TODO  implements AttachmentServiceI 怎么修改 和base项目的耦合
 */
@Service
public class AttachmentService {

    static Logger logger = LoggerFactory.getLogger(AttachmentService.class);

    @Resource
    private BaseAttachmentDao attachmentDao;

    @Autowired
    private AttachmentProperties attachmentProperties;

    /**
     * 上传文件
     *
     * @param file file
     * @param module module
     * @throws IOException some exception
     */
    public Attachment upfile(MultipartFile file, String module) throws IOException {
        if (file == null || file.isEmpty()) {
            return null;
        }
        Attachment attachment = createAttachentFile(file.getOriginalFilename(), module);
        // Kb
        attachment.setSize((int) (file.getSize() / 1024));
        // copy文件流到服务器
        File realFile = attachment.getRealFile();
        FileUtils.copyInputStreamToFile(file.getInputStream(), realFile);
        attachmentDao.insert(attachment);
        attachment.setUrl(attachmentProperties.getVisit() + "/attachment/visit/" + attachment.getId());
        return attachment;

    }

    /**
     * 根据文件原始名称创建附件的文件: 存储位置 = 配置的存储地址 + 模块 + 年/月/日 + 文件
     *
     * @param originalFilename  original File name
     * @param module   文件属于哪个模块
     * @throws IOException some Ex
     */
    private Attachment createAttachentFile(String originalFilename, String module) throws IOException {
        // 去掉一些特殊的字符
        originalFilename = originalFilename.replaceAll("[/?:@=&]", "");
        String filename = CnToSpellUtils.getFullSpell(originalFilename);
        // 生成新的文件名
        filename = CommonUtils.randomUuid() + "-" + filename;
        // 存放的位置
        String position = attachmentProperties.getPosition();
        Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH) + 1;
        int day = c.get(Calendar.DATE);
        StringBuilder relativePath = new StringBuilder();
        if (StringUtils.isNotBlank(module)) {
            relativePath.append(module).append(File.separator);
        }
        relativePath.append(year).append(File.separator).append(month).append(File.separator).append(day);
        // 文件相对路径/绝对路径/文件名
        Attachment attachment = new Attachment(position, relativePath.toString(), filename);
        // ContentType
        attachment.setContentType(CommonUtils.guessContentTypeFromName(originalFilename));
        // originalFilename
        attachment.setOriginalName(originalFilename);
        attachment.setModule(module);
        attachment.setTemporary(1);
        return attachment;
    }

    /**
     * 获取配置的附件主机
     */
    public String getHost() {
        String host = attachmentProperties.getHost();
        if (StringUtils.isBlank(host)) {
            HttpServletRequest request = CommonUtils.currentRequest();
            return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/"
                    + request.getContextPath();
        }
        return host;
    }

    /** 修改附件状态 */
    public void updateTemporary(boolean temporary, int id) {
        attachmentDao.updateTemporary(id, temporary);
    }

    /** 修改附件状态 */
    public void updateTemporary(boolean temporary, List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        String idsStr = ids.stream().map(String::valueOf).collect(Collectors.joining(","));
        attachmentDao.updateTemporary(idsStr, temporary);
    }


    public Attachment selectAttachmentById(Integer id) {
        return attachmentDao.selectAttachmentById(id);
    }

    /**
     * 当访问不存在的附件的时候 返回一个特定的附件:空附件
     */
    public String getBrokenAttachment() {
        String broken = attachmentProperties.getBroken();
        if (StringUtils.isEmpty(broken)) {
            throw new RuntimeException("不没有配置空附件的相对地址");
        }
        return getHost() + broken;
    }

    /* ********************************************************************************************* */

    /**
     * 从对象中获取附件的id集合
     *
     * @see AttachmentFlag
     */
    public static <T> List<Integer> getAttachmentIds(T t) {
        List<Integer> list = new ArrayList<>();
        if (t == null) {
            return list;
        }
        try {
            Class<?> clazz = t.getClass();
            // 向上遍历父类
            for (; clazz != Object.class; clazz = clazz.getSuperclass()) {

                // 暂时不考虑父类的
                Field[] fileds = clazz.getDeclaredFields();
                for (Field f : fileds) {
                    if (f.isAnnotationPresent(AttachmentFlag.class)) {
                        AttachmentFlag flag = f.getAnnotation(AttachmentFlag.class);
                        PropertyDescriptor pd = new PropertyDescriptor(f.getName(), t.getClass());
                        // 获得get方法
                        Method method = pd.getReadMethod();
                        Object value = method.invoke(t);
                        if (value == null || "".equals(value.toString())) {
                            continue;
                        }
                        switch (flag.value()) {
                            case SIGN:
                                if ((value.toString()).matches("\\d+")) {
                                    list.add(Integer.parseInt(value.toString()));
                                }
                                break;
                            case SIGNS:
                                list.addAll(CommonUtils.toIntList(value.toString().split(",")));
                                break;
                            case CONTENT:
                                Integer[] arr = getAttachmentIds(value.toString());
                                if (arr != null && arr.length > 0) {
                                    list.addAll(new ArrayList<>(Arrays.asList(arr)));
                                }
                                break;
                            default:
                                logger.info("反射{}字段{}获得的值为{}", new Object[]{t.getClass(), f.getName(), value});
                                break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(ExceptionUtils.getStackTrace(e));
        }
        return list;
    }

    /** 获得文本内容中的附件id数组 */
    public static Integer[] getAttachmentIds(String content) {
        List<String> list = RegexUtil.getList(content, AttachmentProperties.textAttachmentReg, 1);
        if (list.isEmpty()) {
            return new Integer[]{};
        }
        List<Integer> intList = CommonUtils.toIntList(list.toArray(new String[0]));
        int size = intList.size();
        Integer[] arr = new Integer[size];
        for (int i = 0; i < size; i++) {
            arr[i] = intList.get(i);
        }
        return arr;
    }


    /** 新增对象中的全部附件 */
    public <T> boolean addAttachmengFromObj(T t) {
        List<Integer> idList = getAttachmentIds(t);
        if (idList.isEmpty()) {
            return false;
        }
        String ids = toIds(idList);
        attachmentDao.updateTemporary(ids, false);
        return true;
    }

    /***
     * 删除对象中的全部附件
     */
    public <T> void deleteAttachmengFromObj(List<T> ts) {
        if (ts == null) {
            return;
        }
        List<Integer> list = new ArrayList<Integer>();
        for (T t : ts) {
            list.addAll(getAttachmentIds(t));
        }
        updateTemporary(true, list);
    }

    /**
     * 根据ids查询附件列表
     * @param idList idList
     */
    public List<Attachment> findAttachmentsByIds(List<Integer> idList) {
        List<Attachment> list = attachmentDao.selectAttachmentsByIds(toIds(idList));
        return list;
    }

    /**
     * 转为 1,2,3 形式的字符串
     * @param idList idList
     */
    private String toIds(List<Integer> idList) {
        return idList.stream().map(String::valueOf).collect(Collectors.joining(","));
    }
}
